/**
 * 
 */
package freedom.sample.service.impl;

import java.io.StringReader;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.definition.KnowledgePackage;
import org.drools.definition.rule.Rule;
import org.drools.io.impl.ReaderResource;
import org.drools.runtime.StatefulKnowledgeSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;

import freedom.sample.enums.Operation;
import freedom.sample.event.PerfectExecutable;
import freedom.sample.exception.UnsupportedException;
import freedom.sample.model.RuleInEngine;
import freedom.sample.service.RuleBuilder;
import freedom.sample.service.RuleEngine;

/**
 * @author charlie
 *
 */
public class RuleEngineImpl implements RuleEngine {
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	
	private RuleBuilder ruleBuilder;
	private KnowledgeBase knowledgeBase;
	
	private Set<RuleInEngine> cache = new HashSet<RuleInEngine>();

	/* (non-Javadoc)
	 * @see freedom.sample.RuleEngine#init()
	 */
	@Override
	public void init() throws UnsupportedException {
		knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
		Set<RuleInEngine> ruleInEngines = ruleBuilder.buildRulesForEnginInit();
		for (RuleInEngine ruleInEngine : ruleInEngines) {
			if(ruleInEngine.getOperation() == Operation.ENABLE)
				this.addRule(ruleInEngine, false);
		}
	}

	/* (non-Javadoc)
	 * @see freedom.sample.RuleEngine#refresh()
	 */
	@Override
	public void refresh() throws UnsupportedException {
		Set<RuleInEngine> ruleInEngines = ruleBuilder.buildRulesForEnginRefresh();
		for (RuleInEngine ruleInEngine : ruleInEngines) {
			if(ruleInEngine.getOperation() == Operation.DISABLE)
				this.removeRule(ruleInEngine);
			else if(ruleInEngine.getOperation() == Operation.ENABLE)
				this.addRule(ruleInEngine, false);
			else if(ruleInEngine.getOperation() == Operation.MODIFY)
				this.addRule(ruleInEngine, true);
		}
	}
	
	public void removeRule(RuleInEngine ruleInEngine) {
		String packageName = RuleBuilderImpl.obtainDrlPackageName(ruleInEngine.getOrganNo());
		String ruleName = String.valueOf(ruleInEngine.getRuleTemplate().getCode());
		if(this.knowledgeBase.getRule(packageName, ruleName) != null)
			this.knowledgeBase.removeRule(packageName, ruleName);
	}
	
	public void addRule(RuleInEngine ruleInEngine, boolean replace) {
		if(cache.contains(ruleInEngine)) {
			if(replace) {
				cache.remove(ruleInEngine);
				cache.add(ruleInEngine);
			} else {
				return;
			}
		}
		String packageName = RuleBuilderImpl.obtainDrlPackageName(ruleInEngine.getOrganNo());
		String ruleName = String.valueOf(ruleInEngine.getRuleTemplate().getCode());
		String ruleText = ruleInEngine.getRuleText();
		
		KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		StringReader sr = new StringReader(ruleText);
		ReaderResource rr = new ReaderResource(sr);
		knowledgeBuilder.add(rr, ResourceType.DRL);
		
		if(knowledgeBuilder.hasErrors()) {
			logger.error(knowledgeBuilder.getErrors().toString());
			throw new IllegalArgumentException("Could not add the resource.");
		}
		
		KnowledgePackage newKnowledgePackage = null;
		Rule newRule = null;
		
		Collection<KnowledgePackage> addKnowledgePackages = knowledgeBuilder.getKnowledgePackages();
		Iterator<KnowledgePackage> it = addKnowledgePackages.iterator();
		while(it.hasNext()) {
			newKnowledgePackage = it.next();
			Collection<Rule> addRules = newKnowledgePackage.getRules();
			
			Iterator<Rule> it2 = addRules.iterator();
			while(it2.hasNext()) {
				newRule = it2.next();
				break;
			}
			break;
		}
		
		if(newRule != null) {
			KnowledgePackage oldKnowledgePackage = this.knowledgeBase.getKnowledgePackage(packageName);
			if(oldKnowledgePackage != null) {
				Rule oldRule = this.knowledgeBase.getRule(packageName, ruleName);
				if(oldRule != null) {
					oldKnowledgePackage.getRules().remove(oldRule);
					oldKnowledgePackage.getRules().add(newRule);
				} else {
					oldKnowledgePackage.getRules().add(newRule);
				}
			} else {
				this.knowledgeBase.getKnowledgePackages().add(newKnowledgePackage);
			}
				
		}
	}

	/* (non-Javadoc)
	 * @see freedom.sample.RuleEngine#execute(freedom.sample.Event)
	 */
	@Override
	public void execute(PerfectExecutable event) throws JsonProcessingException {
		event.record();
		StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession();
		session.insert(event);
		session.fireAllRules();
		session.dispose();

	}

	/**
	 * @param ruleBuilder the ruleBuilder to set
	 */
	public void setRuleBuilder(RuleBuilder ruleBuilder) {
		this.ruleBuilder = ruleBuilder;
	}

}
